package create.SingletonPattern.practice;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * @功能职责: 单例模式 --- 懒汉模式
 * @描述：
 * @作者: 郭辉
 * @创建时间: 2020-12-02
 * @copyright Copyright (c) 2020 中国软件与技术服务股份有限公司
 * @company 中国软件与技术服务股份有限公司
 */
public class SingletonPatternLazy {
    /**
     * volatile 作用：
     * 由于对象的 new操作不是原子操作，对于cpu指令，new 分三步：
     * 1，申请内存空间
     * 2，调用构造方法初始化对象
     * 3，将对象名指向内存空间
     * 而再加上指令重排，对于多线程，会存在拿到的对象是没有完成初始化的错误。
     *
     * 而volatile的作用就是禁止指令重排序，避免此错误的产生。
     * */
    private volatile static SingletonPatternLazy singletonPatternLazy;

    /**
     * 创建标志，可以将此变量名设置地复杂一些，避免攻击，只要不反编译，就很难攻击到此
     * */
    private static boolean flag = false;

    //私有构造
    private SingletonPatternLazy(){
        synchronized (SingletonPatternLazy.class){
            if (flag == false){
                flag = true;
            }else {
                throw new RuntimeException("请勿恶意破坏");
            }
        }
    }

    /**
     * 此方法可行，但是加了 synchronized 关键字，影响性能
     * */
    /*public synchronized static SingletonPatternLazy getInstance(){
        if (singletonPatternLazy == null){
            singletonPatternLazy = new SingletonPatternLazy();
        }
        return singletonPatternLazy;
    }*/

    /**
     * DCL 双重检查锁
     * */
    public static SingletonPatternLazy getInstance() {
        /**
         * 第一层判空 是为了提高性能，减小锁的粒度，只有需要new的时候加锁
         * */
        if (singletonPatternLazy == null) {
            /**
             * synchronized 此时将SingletonPatternLazy 类加锁,保证只有一个线程可以new
             * */
            synchronized (SingletonPatternLazy.class) {
                if (singletonPatternLazy == null){
                    singletonPatternLazy = new SingletonPatternLazy();
                }
            }
        }
        return singletonPatternLazy;
    }
}

class TestSingletonPatternLazy{
    public static void main(String[] args) throws Exception {
        SingletonPatternLazy singletonPatternLazy1 = SingletonPatternLazy.getInstance();

        Constructor<SingletonPatternLazy> declaredConstructor = SingletonPatternLazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);

        Field flag = SingletonPatternLazy.class.getDeclaredField("flag");
        flag.setAccessible(true);
        flag.set(flag,false);

//        SingletonPatternLazy singletonPatternLazy1 = declaredConstructor.newInstance();
        SingletonPatternLazy singletonPatternLazy2 = declaredConstructor.newInstance();
        System.out.println(singletonPatternLazy1.hashCode());
        System.out.println(singletonPatternLazy2.hashCode());

        /*for (int i = 0; i < 5; i++) {
            SingletonPatternLazy singletonPatternLazy = declaredConstructor.newInstance();
            System.out.println(singletonPatternLazy.hashCode());
        }*/

        /*
        SingletonPatternLazy singletonPatternLazy2 = declaredConstructor.newInstance();
        System.out.println(singletonPatternLazy1.hashCode());
        System.out.println(singletonPatternLazy2.hashCode());
        */

    }
}
